#include "opcode.h"
#include "flow.h"
#include "lir.h"
#include <list>
#include <map>
#include <cassert>
#include <cstdio>

/// this file transform opcode to x86 lir

using namespace std;

map<int,Label*> X86Encoder::pcToLabel;
map<int,Lir*> X86Encoder::pcToLir;
map<Lir*,Label*> X86Encoder::labelMap;
int X86Encoder::pc = 1;

/// get the operator of binary opcode
LC X86Encoder::getOperator(Opcode *o)
{
    if(dynamic_cast<AddOp*>(o)){
        return ADD;
    } else if (dynamic_cast<SubOp*>(o)) {
        return SUB;
    } else if (dynamic_cast<MulOp*>(o)) {
        return MUL;
    } else if (dynamic_cast<DivOp*>(o)) {
        return DIV;
    } else {
        assert(false && "trying to get operator from non-binary opcode");
    }
}

void X86Encoder::encode(BasicBlock *bb)
{
    Opcode *o;
    RegOperand *regDst,*regArg1,*regArg2;
    Lir *firstLir;

    lirs.clear();

    for(BasicBlock::iterator it = bb->begin();it != bb->end();it++){
        o = *it;
        if(MovOp *op = dynamic_cast<MovOp*>(o)){
            firstLir = new Lir(MOV,new RegOperand(op->dst),new RegOperand(op->src));
            add(firstLir);
        } else if (BinaryOpcode *op = dynamic_cast<BinaryOpcode*>(o)) {
            LC oper = getOperator(op);
            regDst = new RegOperand(op->dst);
            regArg1 = new RegOperand(op->lhs);
            regArg2 = new RegOperand(op->rhs);

            if(*regDst == *regArg1){
                firstLir = new Lir(oper,regDst,regDst,regArg2);
                add(firstLir);
            } else if (*regDst == *regArg2) {
                firstLir = new Lir(oper,regDst,regDst,regArg1);
                add(firstLir);
            } else {
                // x86 instruction can't take 3 registers a time
                firstLir = new Lir(MOV,regDst,regArg1);
                add(firstLir);
                add(new Lir(ADD,regDst,regDst,regArg2));
            }
        } else if (GotoOp *op = dynamic_cast<GotoOp*>(o)) {
            // GotoOp uses only arg1
            // the fake label would be fixed up
            Label *lbl;
            if(pcToLabel.find(op->dstLabel) != pcToLabel.end()){
                lbl = pcToLabel[op->dstLabel];
            } else {
                lbl = new Label(0);
                pcToLabel[op->dstLabel] = lbl;
            }

            AddressOperand *addressOperand = new AddressOperand(lbl);
            firstLir = new Lir(JMP,0,addressOperand);
            add(firstLir);
        } else if (CondGotoOp *op = dynamic_cast<CondGotoOp*>(o)) {
            // test whether cond == 0
            regDst = new RegOperand(op->cond);
            firstLir = new Lir(ORri,regDst,new ConstantOperand<int>(0));
            add(firstLir);
            // JNE and JMP both use arg1 only
            Label *lbl;
            if(pcToLabel.find(op->trueLabel) != pcToLabel.end()){
                lbl = pcToLabel[op->trueLabel];
            } else {
                lbl = new Label(0);
                pcToLabel[op->trueLabel] = lbl;
            }

            AddressOperand *addressOperand = new AddressOperand(lbl);
            add(new Lir(JNE,0,addressOperand));

            if(pcToLabel.find(op->falseLabel) != pcToLabel.end()){
                lbl = pcToLabel[op->trueLabel];
            } else {
                lbl = new Label(0);
                pcToLabel[op->falseLabel] = lbl;
            }
            addressOperand = new AddressOperand(lbl);
            add(new Lir(JMP,0,addressOperand));
        } else if (PushOp *op = dynamic_cast<PushOp*>(o)) {
            regArg1 = new RegOperand(op->index);
            firstLir = new Lir(PUSH,regArg1);
            add(firstLir);
        } else if (PopOp *op = dynamic_cast<PopOp*>(o)) {
            regArg1 = new RegOperand(op->index);
            firstLir = new Lir(POP,regArg1);
            add(firstLir);
        } else if (LoadConstOp *op = dynamic_cast<LoadConstOp*>(o)) {
            regDst = new RegOperand(op->dst);
            firstLir = new Lir(LOADCONST,regDst,new ConstantOperand<int>(op->constIndex));
            add(firstLir);
        } else {
            assert(false && "not recognized opcode to transform");
        }

        pcToLir[pc] = firstLir;
        pc++;
    }
}

void X86Encoder::fixupLabels()
{
    for(map<int,Label*>::iterator it = pcToLabel.begin();it != pcToLabel.end();it++){
        Label *lbl = it->second;

        if(pcToLir.find(it->first) != pcToLir.end()){
            // set the label's corresponding lir
            lbl->lir = pcToLir[it->first];
            // also record the lir's label
            labelMap[lbl->lir] = lbl;
        }
    }
}


